#include "GraphicsItem.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKCore/FITKOperatorRepo.h"

#include "DataCenter/FrameAppData.h"
#include "DataCenter/ComponentAppData.h"
#include "DataCenter/ComponentData.h"
#include "DataCenter/LogData.h"

#include "OperatorsInterface/GraphicsObjectEventOperator.h"
#include "OperatorsInterface/ComponentEventOperator.h"

#include "PortItem.h"
#include "GraphicsScene.h"
#include "NameItem.h"

#include <QPainter>
#include <QSvgRenderer>
#include <QMenu>
#include <QGraphicsSceneEvent>
#include "StateItem.h"

namespace Graphics
{
	class ItemPrivater
	{
	public:
		void initItem(GraphicsItem * item, const QString & componentName)
		{
			/**
			/* get component data by component name
			*/
			auto appData = FITKAPP->getGlobalData()->getData<Data::FrameAppData>(FRAME_APP_DATA_ID);
			if (!appData) { return; }
			auto cptDataMgr = appData->getComponentDataManger();
			if (!cptDataMgr) { return; }
			Data::ComponentData * cptData = cptDataMgr->getDataByName(componentName);

			/**
			* create component instance data and copy form component data
			*/
			_data = new Data::ComponentAppData(item);
			_data->copy(cptData);

			/**
			/* set component item ' attrs. selectable movable focusable and send geometry change info.
			*/
			item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemSendsGeometryChanges);
			item->setToolTip(_data->description());
			item->setAcceptHoverEvents(true);
			item->setComponentName(componentName);

			/**
			/* sub parts of item
			*/
			_nameItem = new NameItem(_data->getDataObjectName(), item);
			_nameItem->setPos(-_nameItem->borderWidth(), _rect.height() + _nameItem->topBorderHeight());
			_portItem = new PortItem(item);
			_portItem->setPos(-_portItem->size() / 2, _rect.width() / 2 - _portItem->size() / 2);
            _statePart = new StateItem(item);
            _statePart->setPos((_rect.width() - _statePart->boundingRect().width()) / 2, -_statePart->boundingRect().height() - 10);

			/**
			* get operator of component event
			*/
			_oper = Core::FITKOperatorRepo::getInstance()->getOperatorT<EventOper::ComponentEventOperator>("SimulationApp");
		}

		/**
		 * @brief     the name of the component. unique id to get component info
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:10
		 */
		QString _componentName;
		
		/**
		 * @brief     data of the component
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:11
		 */
		Data::ComponentAppData * _data{};

		/**
		 * @brief     bounding rect of the component
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:16
		 */
		QRectF _rect{ QRectF(0.0,0.0,80,80) };

		/**
		 * @brief     distance between icon and frame
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:16
		 */
		int _iconBorder{ 10 };

		/**
		 * @brief     weather component is selected
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:15
		 */
		bool _selected{ false };

		/**
		 * @brief     pen to draw frame in normal calculate finished selected component state
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:10
		 */
		QPen _frameNormalPen{ QPen(QColor(56,56,56),2) };
		QPen _frameCalculatePen{ QPen(QColor(56,156,56),2) };
		QPen _frameFinishedPen{ QPen(QColor(56,56,156),2) };
        QPen _frameSelectedPen{ QPen(QColor(255,56,199),2,Qt::SolidLine) };

		/**
		 * @brief     state of component
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:10
		 */
		ComponentState _componentState{ Component_Normal_State };

		/**
		 * @brief     is this graphics item is current item
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-9-10 10:11
		 */
		bool _isCurrentComponent{ false };

		/**
		 * @brief     name part of component
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:09
		 */
		NameItem * _nameItem{};

		/**
		 * @brief     port part of component
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-31 12:14
		 */
		PortItem * _portItem{};

        /**
         * @brief     state part of component
         * @author    maguiwa(maguiwa520@163.com)
         * @date      2024-9-26 10:41
         */
        StateItem * _statePart{};

		/**
		 * @brief     operator of component event
		 * @author    maguiwa(maguiwa520@163.com)
		 * @date      2024-8-29 17:08
		 */
		EventOper::ComponentEventOperator * _oper{};
	};


	GraphicsItem::GraphicsItem(const QString & componentName, QGraphicsItem *parent /*= nullptr*/)
		: QGraphicsObject(parent)
		, _privater(new ItemPrivater())
	{
		_privater->initItem(this, componentName);
	}

	GraphicsItem::~GraphicsItem()
	{
		if (_privater)
		{
			delete _privater;
			_privater = nullptr;
		}
	}

	QString GraphicsItem::componentName() const
	{
		return _privater->_componentName;
	}

	void GraphicsItem::setComponentName(const QString & name)
	{
		_privater->_componentName = name;
		update();
	}

	ComponentState GraphicsItem::componentState() const
	{
		return _privater->_componentState;
	}

	void GraphicsItem::setComponentState(ComponentState state)
	{
		_privater->_componentState = state;
		update();
	}

    void GraphicsItem::setComponentInputState(ComponentInputState inputState)
    {
        _privater->_statePart->setGraphicsItemState(inputState);
    }

    bool GraphicsItem::currentComponentItem() const
	{
		return _privater->_isCurrentComponent;
	}

	void GraphicsItem::setCurrentComponentItem(bool current)
	{
		_privater->_isCurrentComponent = current;
		update();
	}

	void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /* = nullptr */)
	{
		Q_UNUSED(option);
		Q_UNUSED(widget);

		painter->save();
		painter->setRenderHint(QPainter::Antialiasing);

		/// 1. draw frame
		if (isSelected())
		{
			painter->setPen(_privater->_frameSelectedPen);
		}
		else
		{
			switch (_privater->_componentState)
			{
			case Component_Normal_State:
				painter->setPen(_privater->_frameNormalPen);
				break;
			case Component_Calculate_State:
				painter->setPen(_privater->_frameCalculatePen);
				break;
			case Component_Finished_State:
				painter->setPen(_privater->_frameFinishedPen);
				break;
			}
		}
		painter->drawRoundRect(_privater->_rect);

		/// 2.draw current item
		if (_privater->_isCurrentComponent)
		{
			painter->setBrush(QBrush(QColor(165, 215, 165)));
			painter->drawRoundRect(_privater->_rect);
		}

		/// 3. draw icon
		QSvgRenderer renderer;
		QString svg = _privater->_data->svg();
		renderer.load(svg);
		renderer.render(painter, _privater->_rect.adjusted(_privater->_iconBorder, _privater->_iconBorder, -_privater->_iconBorder, -_privater->_iconBorder));
		
		painter->restore();
	}

	QRectF GraphicsItem::boundingRect() const
	{
		return _privater->_rect;
	}

        Data::ComponentAppData * GraphicsItem::componentAppData() const
        {
                return _privater->_data;
        }

	Graphics::PortItem * GraphicsItem::portPart() const
	{
		return _privater->_portItem;
	}

	Graphics::NameItem * GraphicsItem::namePart() const
	{
		return _privater->_nameItem;
	}

	void GraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
	{
		QGraphicsObject::mouseDoubleClickEvent(event);
		if (!_privater->_oper) { return; }
		_privater->_oper->doubleClicked(this);
	}

    void GraphicsItem::menuItemClicked()
    {
        if (!_privater->_oper) { return; }
        QAction * action = qobject_cast<QAction *> (sender());
        if (action)
        {
            if (action->text() == tr("Open Project"))
            {
                _privater->_oper->openProject(this);
            }
            else if (action->text() == tr("Run"))
            {
                _privater->_oper->run(this);
            }
        }
    }

	QVariant GraphicsItem::itemChange(GraphicsItemChange change, const QVariant & value)
	{
		/// support item position change
		if ((change == ItemPositionChange || change == ItemPositionHasChanged) && scene())
		{
			GraphicsScene * _scene = qobject_cast<GraphicsScene *>(scene());
			if (_scene)
				_scene->updateLineItem(this);
		}
		return QGraphicsItem::itemChange(change, value);
	}

	void GraphicsItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
	{
		Q_UNUSED(event);

		QMenu menu;
        QAction * action = nullptr;

        bool bOk = _privater->_oper->rightButtonMenu(this);
        if (!bOk) { return; }

        if (_privater->_oper->hasArgs(tr("Open Project")))
        {
            action = menu.addAction(tr("Open Project"));
            connect(action, SIGNAL(triggered()), this, SLOT(menuItemClicked()));
        }
        if (_privater->_oper->hasArgs(tr("Run")))
        {
            action = menu.addAction(tr("Run"));
            connect(action, SIGNAL(triggered()), this, SLOT(menuItemClicked()));
        }

		menu.exec(QCursor::pos());
	}

}
